/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>

#include "log.h"
#include "bts_le_gap.h"
#include "bts_gatt_server.h"
#include "ohos_bt_gatt_server.h"

static uint8_t g_descVal[]  = {0x00, 0x00};
static uint8_t g_charaVal[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static int g_deleteServiceHandle = 0;
static uint16_t g_serverRequestId = 0;
static BtGattServerCallbacks *g_bleGattsCb = NULL;
static BdAddr g_bdAddr = {0};

static uint32_t Convert_To_Permissions(uint32_t permissions)
{
    uint32_t perm = 0;
    if (permissions & OHOS_GATT_PERMISSION_READ) {
        perm |= GATT_ATTRIBUTE_PERMISSION_READ;
    }
    if (permissions & OHOS_GATT_PERMISSION_READ_ENCRYPTED) {
        perm |= (GATT_ATTRIBUTE_PERMISSION_READ | GATT_ATTRIBUTE_PERMISSION_ENCRYPTION_NEED);
    }
    if (permissions & OHOS_GATT_PERMISSION_READ_ENCRYPTED_MITM) {
        perm |= (GATT_ATTRIBUTE_PERMISSION_READ |
            GATT_ATTRIBUTE_PERMISSION_ENCRYPTION_NEED | GATT_ATTRIBUTE_PERMISSION_MITM_NEED);
    }
    if (permissions & OHOS_GATT_PERMISSION_WRITE) {
        perm |= GATT_ATTRIBUTE_PERMISSION_WRITE;
    }
    if (permissions & OHOS_GATT_PERMISSION_WRITE_ENCRYPTED) {
        perm |= (GATT_ATTRIBUTE_PERMISSION_WRITE | GATT_ATTRIBUTE_PERMISSION_ENCRYPTION_NEED);
    }
    if (permissions & OHOS_GATT_PERMISSION_WRITE_ENCRYPTED_MITM) {
        perm |= (GATT_ATTRIBUTE_PERMISSION_WRITE |
            GATT_ATTRIBUTE_PERMISSION_ENCRYPTION_NEED | GATT_ATTRIBUTE_PERMISSION_MITM_NEED);
    }
    return perm;
}

static void Convert_To_Uuid(bt_uuid_t *uuid, BtUuid *uuidData)
{
    uuid->uuid_len = uuidData->uuidLen;

    if (uuidData->uuidLen == 2) {
        uuid->uuid[0] = uuidData->uuid[1];
        uuid->uuid[1] = uuidData->uuid[0];
    } else {
        (void)memcpy_s(uuid->uuid, uuidData->uuidLen, (uint8_t *)uuidData->uuid, uuidData->uuidLen);
    }
}

int BleGattsRegister(BtUuid appUuid)
{
    uint8_t serverId = 0;
    bt_uuid_t uuid = {0};

    HILOG_DEBUG(0, "[bluetooth] BleGattsRegister\n");

    Convert_To_Uuid(&uuid, &appUuid);
    return gatts_register_server(&uuid, &serverId);
}

int BleGattsUnRegister(int serverId)
{
    HILOG_DEBUG(0, "[bluetooth] BleGattsUnRegister\n");

    return gatts_unregister_server(serverId);
}

int BleGattsAddService(int serverId, BtUuid srvcUuid, bool isPrimary, int number)
{
    bt_uuid_t uuid = {0};

    HILOG_DEBUG(0, "[bluetooth] BleGattsAddService\n");
    
    Convert_To_Uuid(&uuid, &srvcUuid);
    return gatts_add_service(serverId, &uuid, isPrimary);
}

int BleGattsAddCharacteristic(int serverId, int srvcHandle, BtUuid characUuid,
                              int properties, int permissions)
{
    bt_uuid_t uuid = {0};
    gatts_add_chara_info_t charaInfo = {0};

    HILOG_DEBUG(0, "[bluetooth] BleGattsAddCharacteristic\n");

    Convert_To_Uuid(&uuid, &characUuid);

    charaInfo.chara_uuid   = uuid;
    charaInfo.properties   = properties;
    charaInfo.permissions  = Convert_To_Permissions(permissions);
    charaInfo.value_len    = sizeof(g_charaVal);
    charaInfo.value        = g_charaVal;

    return gatts_add_characteristic(serverId, srvcHandle, &charaInfo);
}

int BleGattsAddDescriptor(int serverId, int srvcHandle, BtUuid descUuid, int permissions)
{
    bt_uuid_t uuid = {0};
    gatts_add_desc_info_t descriptor = {0};

    HILOG_DEBUG(0, "[bluetooth] BleGattsAddDescriptor\n");

    Convert_To_Uuid(&uuid, &descUuid);

    descriptor.desc_uuid    = uuid;
    descriptor.permissions  = Convert_To_Permissions(permissions);
    descriptor.value_len    = sizeof(g_descVal);
    descriptor.value        = g_descVal;

    return gatts_add_descriptor(serverId, srvcHandle, &descriptor);
}

int BleGattsStartService(int serverId, int srvcHandle)
{
    HILOG_DEBUG(0, "[bluetooth] BleGattsStartService\n");

    return gatts_start_service(serverId, srvcHandle);
}

int BleGattsStopService(int serverId, int srvcHandle)
{
    HILOG_DEBUG(0, "[bluetooth] BleGattsStopService\n");

    return gatts_stop_service(serverId, srvcHandle);
}

int BleGattsDeleteService(int serverId, int srvcHandle)
{
    HILOG_DEBUG(0, "[bluetooth] BleGattsDeleteService\n");

    g_deleteServiceHandle = srvcHandle;
    return gatts_delete_all_services(serverId);
    // return gatts_delete_service(serverId, srvcHandle);
}

int BleGattsSendResponse(int serverId, GattsSendRspParam *param)
{
    gatts_send_rsp_t rspParam = {0};

    HILOG_DEBUG(0, "[bluetooth] BleGattsSendResponse\n");

    rspParam.request_id = g_serverRequestId;
    rspParam.status = 0;
    rspParam.offset = 0;
    rspParam.value_len = param->valueLen;
    rspParam.value = (uint8_t *)param->value;

    return gatts_send_response(serverId, param->connectId, &rspParam);
}

int BleGattsSendIndication(int serverId, GattsSendIndParam *param)
{
    errcode_t ret = ERRCODE_BT_FAIL;
    gatts_ntf_ind_t ntfParam = {0};

    HILOG_DEBUG(0, "[bluetooth] BleGattsSendIndication\n");

    ntfParam.attr_handle   = param->attrHandle;
    ntfParam.value_len     = param->valueLen;
    ntfParam.value         = (uint8_t *)param->value;

    ret = gatts_notify_indicate(serverId, param->connectId, &ntfParam);
    if (g_bleGattsCb->indicationSentCb) {
        g_bleGattsCb->indicationSentCb(param->connectId, ret);
    }

    return ret;
}

void gatt_state_change_callback(int connId, const BdAddr *bdAddr, int state)
{
    HILOG_DEBUG(0, "[bluetooth] gatt_state_change_callback gatt state:%d.\n", state);

    if (state == GAP_BLE_STATE_CONNECTED) {
        memcpy_s(&g_bdAddr, sizeof(BdAddr), bdAddr, sizeof(BdAddr));
        if (g_bleGattsCb->connectServerCb) {
            g_bleGattsCb->connectServerCb(connId, 0, bdAddr);
        }
    } else if (state == GAP_BLE_STATE_DISCONNECTED) {
        memset(&g_bdAddr, 0, sizeof(BdAddr));
        if (g_bleGattsCb->disconnectServerCb) {
            g_bleGattsCb->disconnectServerCb(connId, 0, bdAddr);
        }
    }
}

static void add_service_callback(uint8_t server_id, bt_uuid_t *uuid, uint16_t handle, errcode_t status)
{
    uint8_t swapUuid[2];
    BtUuid bt_uuid = {uuid->uuid_len, uuid->uuid};

    if (uuid->uuid_len == 2) {
        swapUuid[1] = uuid->uuid[0];
        swapUuid[0] = uuid->uuid[1];
        bt_uuid.uuid = swapUuid;
    }

    HILOG_DEBUG(0, "[bluetooth] add_service_callback server:%d srvc_hdl:%d\n", server_id, handle);

    if (g_bleGattsCb->serviceAddCb) {
        g_bleGattsCb->serviceAddCb(status, server_id, &bt_uuid, handle);
    }
}

static void add_characteristic_callback(uint8_t server_id, bt_uuid_t *uuid, uint16_t service_handle,
    gatts_add_character_result_t *result, errcode_t status)
{
    uint8_t swapUuid[2];
    BtUuid bt_uuid = {uuid->uuid_len, uuid->uuid};

    if (uuid->uuid_len == 2) {
        swapUuid[1] = uuid->uuid[0];
        swapUuid[0] = uuid->uuid[1];
        bt_uuid.uuid = swapUuid;
    }

    HILOG_DEBUG(0, "[bluetooth] add_characteristic_callback "
        "server:%d srvc_hdl:%d char_hdl:%d char_val_hdl:%d uuid_len:%d \n",
        server_id, service_handle, result->handle, result->value_handle, uuid->uuid_len);

    if (g_bleGattsCb->characteristicAddCb) {
        g_bleGattsCb->characteristicAddCb(status, server_id, &bt_uuid, service_handle, result->value_handle);
    }
}

static void add_descriptor_callback(uint8_t server_id, bt_uuid_t *uuid, uint16_t service_handle,
    uint16_t handle, errcode_t status)
{
    uint8_t swapUuid[2];
    BtUuid bt_uuid = {uuid->uuid_len, uuid->uuid};

    if (uuid->uuid_len == 2) {
        swapUuid[1] = uuid->uuid[0];
        swapUuid[0] = uuid->uuid[1];
        bt_uuid.uuid = swapUuid;
    }

    HILOG_DEBUG(0, "[bluetooth] add_descriptor_callback "
        "server:%d srv_hdl:%d desc_hdl:%d uuid_len:%d.\n",
        server_id, service_handle, handle, uuid->uuid_len);

    if (g_bleGattsCb->descriptorAddCb) {
        g_bleGattsCb->descriptorAddCb(status, server_id, &bt_uuid, service_handle, handle);
    }
}

static void start_service_callback(uint8_t server_id, uint16_t handle, errcode_t status)
{
    HILOG_DEBUG(0, "[bluetooth] start_service_callback server:%d srv_hdl:%d status:%d\n", 
        server_id, handle, status);

    if (g_bleGattsCb->serviceStartCb) {
        g_bleGattsCb->serviceStartCb(status, server_id, handle);
    }
}

static void stop_service_callback(uint8_t server_id, uint16_t handle, errcode_t status)
{
    HILOG_DEBUG(0, "[bluetooth] stop_service_callback server:%d srv_hdl:%d status:%d\n",
        server_id, handle, status);

    if (g_bleGattsCb->serviceStopCb) {
        g_bleGattsCb->serviceStopCb(status, server_id, handle);
    }
}

static void delete_service_callback(uint8_t server_id, errcode_t status)
{
    HILOG_DEBUG(0, "[bluetooth] delete_service_callback server:%d srv_hdl:%d status:%d\n",
        server_id, status);

    if (g_bleGattsCb->serviceDeleteCb) {
        g_bleGattsCb->serviceDeleteCb(status, server_id, g_deleteServiceHandle);
    }
}

static void read_request_callback(uint8_t server_id, uint16_t conn_id, gatts_req_read_cb_t *read_cb_para,
    errcode_t status)
{
    BtReqReadCbPara para = {0};
    BdAddr addrBuff = {0};
    para.bdAddr = &addrBuff;
    memcpy_s(para.bdAddr, sizeof(BdAddr), &g_bdAddr, sizeof(BdAddr));
    HILOG_DEBUG(0, "[bluetooth] read_request_callback server_id:%d conn_id:%d status:%d\n",
        server_id, conn_id, status);

    para.connId = conn_id;
    para.transId = OHOS_BT_TRANSPORT_LE;
    para.attrHandle = read_cb_para->handle;
    para.offset = read_cb_para->offset;
    para.isLong = read_cb_para->is_long;
    g_serverRequestId = read_cb_para->request_id;
    if (g_bleGattsCb->requestReadCb) {
        g_bleGattsCb->requestReadCb(para);
    }
}

static void write_request_callback(uint8_t server_id, uint16_t conn_id, gatts_req_write_cb_t *write_cb_para,
    errcode_t status)
{
    BtReqWriteCbPara para;
    BdAddr addrBuff = {0};
    para.bdAddr = &addrBuff;
    memcpy_s(para.bdAddr, sizeof(BdAddr), &g_bdAddr, sizeof(BdAddr));
    HILOG_DEBUG(0, "[bluetooth] write_request_callback request_id:%d att_handle:%d data_len:%d\n",
        write_cb_para->request_id, write_cb_para->handle, write_cb_para->length);

    para.connId = conn_id;
    para.transId = OHOS_BT_TRANSPORT_LE;
    para.attrHandle = write_cb_para->handle;
    para.offset = write_cb_para->offset;
    para.length = write_cb_para->length;
    para.needRsp = write_cb_para->need_rsp;
    para.isPrep = write_cb_para->is_prep;
    para.value = write_cb_para->value;
    g_serverRequestId = write_cb_para->request_id;
    if (g_bleGattsCb->requestWriteCb) {
        g_bleGattsCb->requestWriteCb(para);
    }
}

static void mtu_changed_callback(uint8_t server_id, uint16_t conn_id, uint16_t mtu_size, errcode_t status)
{
    HILOG_DEBUG(0, "[bluetooth] mtu_changed_callback server_id:%d conn_id:%d mtu_size:%d status:%d\n",
        server_id, conn_id, mtu_size, status);

    if (g_bleGattsCb->mtuChangeCb) {
        g_bleGattsCb->mtuChangeCb(conn_id, mtu_size);
    }
}

static gatts_callbacks_t g_gattCallback = {
    .add_service_cb          = add_service_callback,
    .add_characteristic_cb   = add_characteristic_callback,
    .add_descriptor_cb       = add_descriptor_callback,
    .start_service_cb        = start_service_callback,
    .stop_service_cb         = stop_service_callback,
    .delete_service_cb       = delete_service_callback,
    .read_request_cb         = read_request_callback,
    .write_request_cb        = write_request_callback,
    .mtu_changed_cb          = mtu_changed_callback,
};

int BleGattsRegisterCallbacks(BtGattServerCallbacks *func)
{
    if (func == NULL) {
        HILOG_ERROR(0, "[bluetooth] BleGattsRegisterCallbacks func is NULL\n");
        return -1;
    }

    HILOG_DEBUG(0, "[bluetooth] BleGattsRegisterCallbacks\n");

    g_bleGattsCb = func;
    return gatts_register_callbacks(&g_gattCallback);
}

int BleGattsDisconnect(int serverId, BdAddr bdAddr, int connId)
{
    bd_addr_t bd_addr = { 0 };
    bd_addr.type = BT_ADDRESS_TYPE_PUBLIC_DEVICE_ADDRESS;
    memcpy_s(bd_addr.addr, BD_ADDR_LEN, bdAddr.addr, BD_ADDR_LEN);
    return gap_ble_disconnect_remote_device(&bd_addr);
}
